package com.example;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.json.JsonMapper;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;

/**
 * Http请求执行器
 *
 * @author 凡行
 */
public class JdkHttpExecutor {
    private final HttpClient client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NEVER).build();
    private final JsonMapper jsonMapper;
    private final String endpoint;

    public JdkHttpExecutor(JsonMapper jsonMapper, String endpoint) {
        this.jsonMapper = jsonMapper;
        this.endpoint = endpoint;
    }

    public <T> T get(String uriQuery, Map<String, String> headers, JavaType type) {
        try {
            byte[] result = this.execute(createRequestBuilder(uriQuery, headers).GET().build());
            return this.decode(result, type);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public <T> T post(String uriQuery, Map<String, String> headers, Object body, JavaType type) {
        try {
            byte[] result = this.execute(createRequestBuilder(uriQuery, headers).POST(bodyPublisher(body)).build());
            return this.decode(result, type);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public <T> T delete(String uriQuery, Map<String, String> headers, JavaType type) {
        try {
            byte[] result = this.execute(createRequestBuilder(uriQuery, headers).DELETE().build());
            return this.decode(result, type);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public <T> T put(String uriQuery, Map<String, String> headers, Object body, JavaType type) {
        try {
            byte[] result = this.execute(createRequestBuilder(uriQuery, headers).PUT(bodyPublisher(body)).build());
            return this.decode(result, type);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private byte[] execute(HttpRequest request) throws IOException, InterruptedException {
        HttpResponse<byte[]> response = this.client.send(request, HttpResponse.BodyHandlers.ofByteArray());
        if (response.statusCode() != 200) {
            throw new RuntimeException("http execute error");
        }
        return response.body();
    }

    private HttpRequest.Builder createRequestBuilder(String uriQuery, Map<String, String> headers) {
        HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(this.endpoint + uriQuery));
        headers.forEach(builder::setHeader);
        return builder;
    }

    private HttpRequest.BodyPublisher bodyPublisher(Object body) {
        return HttpRequest.BodyPublishers.ofByteArray(encode(body));
    }

    protected byte[] encode(Object value) {
        try {
            return jsonMapper.writeValueAsBytes(value);
        } catch (JsonProcessingException e) {
            throw new IllegalStateException("json编码错误");
        }
    }

    protected <T> T decode(byte[] body, JavaType type) {
        try {
            return jsonMapper.readValue(body, type);
        } catch (IOException e) {
            throw new IllegalStateException("json解码错误");
        }
    }
}
